#pragma once
#include <iostream>
#include <string>
#include <memory>
#include <cstdlib>
#include <thread>
#include <functional>
#include <vector>
#include <sys/timerfd.h>
#include <unistd.h>
#include <mysql/mysql.h>

namespace ns_mysql
{
    enum class STATU
    {
        INIT_ERROR,
        INIT_SUCCESS,
        CONNECTED,
        DISCONNECTED
    };
    // 心跳机制
    static const int default_init_cnt = 3;

    class MySQL;

    struct HeartbeatDetect
    {
        std::thread _check;
        std::string _sql;
        bool _isrunning;
        int _timerfd;
        MySQL *_my; // 使用指针来避免循环依赖
        void Read();
        HeartbeatDetect(MySQL *mysql)
            : _my(mysql),
              _check(std::bind(&HeartbeatDetect::Read, this)),
              _isrunning(true)
        {
            _timerfd = ::timerfd_create(CLOCK_MONOTONIC, 0);
            if (_timerfd < 0)
            {
                std::cerr << "创建定时器失败" << std::endl;
                abort();
            }
            struct itimerspec new_settimer;
            new_settimer.it_interval.tv_sec = 5; // TODO
            new_settimer.it_interval.tv_nsec = 0;

            new_settimer.it_value.tv_sec = 5;
            new_settimer.it_value.tv_nsec = 0;
            int n = ::timerfd_settime(_timerfd, 0, &new_settimer, nullptr);
            if (n != 0)
            {
                std::cerr << "设置超时间隔失败" << std::endl;
                abort();
            }
        }
        ~HeartbeatDetect()
        {
            _isrunning = false;
            _check.detach();
        }
    };

    class MySQL
    {
    private:
        MYSQL *_my;
        STATU _statu;
        int _init_cnt;
        int _conn_cnt;
        HeartbeatDetect _heart;

    public:
        std::string _host;
        std::string _user;
        std::string _password;
        std::string _database;
        int _port;

    public:
        bool RetryConnect()
        {
            if (_statu != STATU::INIT_SUCCESS)
                return false;
            while (mysql_real_connect(_my,
                                      _host.c_str(),
                                      _user.c_str(),
                                      _password.c_str(),
                                      _database.c_str(),
                                      _port,
                                      nullptr,
                                      0) == nullptr &&
                   _conn_cnt)
            {
                // 重连
                --_conn_cnt;
            }
            if (_conn_cnt == 0)
            {
                // TODO,之后可以改成日志
                std::cerr << "MySQL Connect error" << std::endl;
                return false;
            }
            // 这里直接使用默认的重试次数即可
            _conn_cnt = default_init_cnt;
            _statu = STATU::CONNECTED;
            return true;
        }
        void RetryInit()
        {
            while (_my == nullptr && _init_cnt)
            {
                _my = mysql_init(nullptr);
                --_init_cnt;
            }
            if (_init_cnt == 0 && _my == nullptr)
            {
                _statu = STATU::INIT_ERROR;
                abort();
            }
            _statu = STATU::INIT_SUCCESS;
        }

    public:
        MySQL(int init_cnt = default_init_cnt, int con_cnt = default_init_cnt)
            : _my(nullptr),
              _init_cnt(init_cnt),
              _conn_cnt(con_cnt),
              _heart(this)
        {
            RetryInit();
            _init_cnt = init_cnt;
        }
        MYSQL *MysqlPtr()
        {
            return _my;
        }
        bool ConnectMysql(const std::string &host,
                          const std::string &user,
                          const std::string &password,
                          const std::string &database,
                          unsigned int port,
                          const char *unix_socket = nullptr,
                          unsigned int clientflag = 0)
        {
            _host = host;
            _user = user;
            _password = password;
            _database = database;
            _port = port;
            return RetryConnect();
        }
        ~MySQL()
        {
            mysql_close(_my);
        }

        STATU Statu()
        {
            return _statu;
        }

        bool Query(const std::string &sql) // 建议使用c++11的R原始字符串
        {
            int n = mysql_query(_my, sql.c_str());
            if (n != 0)
            {
                std::cout << "sql:" << sql << "语句执行失败" << std::endl;
                return false;
            }
            std::cout << "sql:" << sql << "语句执行成功" << std::endl;
            return true;
        }
        bool Query(const std::string &sql, std::vector<MYSQL_ROW> *res, int *filed)
        {
            int n = mysql_query(_my, sql.c_str());
            if (n != 0)
            {
                // TODO可以改成日志
                std::cout << "sql:" << sql << "语句执行失败" << std::endl;
                return false;
            }
            MYSQL_RES *r = mysql_store_result(_my);
            if (r == nullptr)
            {
                // TODO可以改成日志
                std::cout << "保存结果集错误" << std::endl;
                return false;
            }
            int rows = mysql_num_rows(r);
            *filed = mysql_num_fields(r);
            for (int i = 0; i < rows; i++)
            {
                MYSQL_ROW row = mysql_fetch_row(r);
                res->push_back(std::move(row));
            }
            return true;
        }
    };

    void HeartbeatDetect::Read()
    {
        while (_isrunning)
        {
            uint64_t val = 0;
            ssize_t n = ::read(_timerfd, &val, sizeof(val));
            if (!_isrunning)
            {
                 ::close(_timerfd);
                 break;
            }
            if (n >= 0)
            {
                std::string sql = "select 1";
                if (_my)
                {
                    if (!_my->Query(sql))
                    {
                        std::cerr << "心跳检测发送sql失败" << std::endl;
                        // 说明状态异常
                        const char *ret = mysql_stat(_my->MysqlPtr());
                        if (ret == nullptr)
                        {
                            _my->RetryConnect();
                        }
                        else
                        {
                            std::cerr << "未知错误" << std::endl;
                            continue;
                        }
                    }
                    else std::cout<<"发送心跳检测done"<<std::endl;
                }
            }
        }
    }
}